﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Interactivity;
using AvalonDock;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using System.Collections;
using System.Windows.Input;
using Inovout.Gui.Wpf.Views;
using Inovout.Gui.Wpf.ViewModels;

namespace Inovout.Portal.Desktop.Controls
{
    public class ViewModelsOnAvalonDockBehavior : Behavior<DockingManager>
    {
        private ObservableCollection<DocumentContent> documents;
        private ICollectionView documentCollectionView;
        private ObservableCollection<DockableContent> pads;
        private ICollectionView padCollectionView;

        private DockingManager DockManager { get; set; }
        public IEnumerable<object> DocumentsSource
        {
            get { return (IEnumerable<object>)GetValue(DocumentsSourceProperty); }
            set { SetValue(DocumentsSourceProperty, value); }
        }

        public static readonly DependencyProperty DocumentsSourceProperty =
            DependencyProperty.Register("DocumentsSource", typeof(IEnumerable<object>), typeof(ViewModelsOnAvalonDockBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback(OnDocumentsSourceChanged)));

        public IEnumerable<object> PadsSource
        {
            get { return (IEnumerable<object>)GetValue(PadsSourceProperty); }
            set { SetValue(PadsSourceProperty, value); }
        }

        public static readonly DependencyProperty PadsSourceProperty =
            DependencyProperty.Register("PadsSource", typeof(IEnumerable<object>), typeof(ViewModelsOnAvalonDockBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback(OnPadsSourceChanged)));

        private static void OnDocumentsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != null)
            {
                var behavior = obj as ViewModelsOnAvalonDockBehavior;
                behavior.documents = new ObservableCollection<DocumentContent>();
                if (behavior.DockManager != null)
                {
                    behavior.DockManager.DocumentsSource = behavior.documents;
                }
                behavior.documentCollectionView = CollectionViewSource.GetDefaultView(args.NewValue);
                if (behavior.documentCollectionView != null)
                {
                    behavior.documentCollectionView.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(behavior.OnDocumentsCollectionChanged);
                    behavior.documentCollectionView.CurrentChanged += new EventHandler(behavior.OnDocumentsCurrentChanged);
                }
                behavior.AddNewDocuments(args.NewValue as IEnumerable);
            }
        }
        void documentContent_Closing(object sender, CancelEventArgs e)
        {
            if (DocumentClosingCommand.CanExecute((sender as DocumentContent).Content))
            {
                DocumentClosingCommand.Execute((sender as DocumentContent).Content);
            }
            else { e.Cancel = true; }
        }
        protected override void OnAttached()
        {
            base.OnAttached();
            this.DockManager = this.AssociatedObject;
            this.DockManager.ActiveContentChanged += new EventHandler(DockingManagerActiveContentChanged);
            if (DockManager != null && documents != null)
                DockManager.DocumentsSource = documents;
        }

        void DockingManagerActiveContentChanged(object sender, EventArgs e)
        {
            if (this.DockManager.ActiveContent is DocumentContent)
            {
                documentCollectionView.MoveCurrentTo(this.DockManager.ActiveContent.Content);
            }
            else if (this.DockManager.ActiveContent is DockableContent)
            {
                padCollectionView.MoveCurrentTo(this.DockManager.ActiveContent.Content);
            }
        }

        private void AddNewDocuments(IEnumerable documents)
        {
            foreach (var document in documents)
            {
                DocumentContent documentContent = new DocumentContent();
                documentContent.Content = document;
                SetTitleAndIcon(document, documentContent);
                if (DocumentClosingCommand != null)
                {
                    documentContent.Closing += new EventHandler<CancelEventArgs>(documentContent_Closing);
                }
                this.documents.Add(documentContent);
            }
        }
        public static readonly DependencyProperty DocumentClosingCommandProperty =
             DependencyProperty.Register("DocumentClosingCommand", typeof(ICommand), typeof(ViewModelsOnAvalonDockBehavior),
             new UIPropertyMetadata(null));

        public ICommand DocumentClosingCommand
        {
            get { return (ICommand)GetValue(DocumentClosingCommandProperty); }
            set { SetValue(DocumentClosingCommandProperty, value); }
        }

        public ICommand PadClosingCommand
        {
            get { return (ICommand)GetValue(PadClosingCommandProperty); }
            set { SetValue(PadClosingCommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DocumentClosingCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PadClosingCommandProperty =
            DependencyProperty.Register("PadClosingCommand", typeof(ICommand), typeof(ViewModelsOnAvalonDockBehavior), new UIPropertyMetadata(null));

        [Bindable(false)]
        public string TitleMemberPath
        {
            get { return (string)GetValue(TitleMemberPathProperty); }
            set { SetValue(TitleMemberPathProperty, value); }
        }
        public static readonly DependencyProperty TitleMemberPathProperty =
    DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(ViewModelsOnAvalonDockBehavior));
        [Bindable(false)]
        public string IconMemberPath
        {
            get { return (string)GetValue(IconMemberPathProperty); }
            set { SetValue(IconMemberPathProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IconMemberPath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IconMemberPathProperty =
            DependencyProperty.Register("IconMemberPath", typeof(string), typeof(ViewModelsOnAvalonDockBehavior));

        private void SetTitleAndIcon(object viewModel, ManagedContent content)
        {
            if (!string.IsNullOrWhiteSpace(TitleMemberPath))
            {
                Binding titleBinding = new Binding(TitleMemberPath);
                titleBinding.Source = viewModel;
                content.SetBinding(ManagedContent.TitleProperty, titleBinding);
            }
            if (!string.IsNullOrWhiteSpace(((DomainViewModel)viewModel).Icon))
            {
                Binding iconBinding = new Binding(((DomainViewModel)viewModel).Icon);
                iconBinding.Source = viewModel;
                content.SetBinding(ManagedContent.IconProperty, iconBinding);
            }
        }
        void OnDocumentsCurrentChanged(object sender, EventArgs e)
        {
            var activeDocumentContent = documents.FirstOrDefault(x => x.Content == documentCollectionView.CurrentItem);
            this.DockManager.ActiveDocument = activeDocumentContent;
        }
        void OnDocumentsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    this.AddNewDocuments(e.NewItems);
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }

        private static void OnPadsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != null)
            {
                var behavior = obj as ViewModelsOnAvalonDockBehavior;
                behavior.pads = new ObservableCollection<DockableContent>();
                behavior.padCollectionView = CollectionViewSource.GetDefaultView(args.NewValue);
                if (behavior.padCollectionView != null)
                {
                    behavior.padCollectionView.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(behavior.OnPadsCollectionChanged);
                    behavior.padCollectionView.CurrentChanged += new EventHandler(behavior.OnPadsCurrentChanged);
                }
                behavior.AddNewPads(args.NewValue as IEnumerable);
            }
        }
        void OnPadsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    this.AddNewPads(e.NewItems);
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }
        public DockableStyle DockingStyle
        {
            get { return (DockableStyle)GetValue(DockingStyleProperty); }
            set { SetValue(DockingStyleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DockingPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DockingStyleProperty =
            DependencyProperty.Register("DockingStyle", typeof(DockableStyle), typeof(ViewModelsOnAvalonDockBehavior),
            new UIPropertyMetadata(DockableStyle.Dockable));

        private void AddNewPads(IEnumerable pads)
        {
            foreach (var pad in pads)
            {
                var padContent = new DockableContent();
                View view = null; // ComponentManager.Resolve<BaseView>(((ModelViewModel)pad).WorkItemContext.ViewName.ToLower() + "view");
                if (view == null)
                {
                    padContent.Content = pad;
                }
                else
                {
                    view.DataContext = pad;
                    padContent.Content = view;
                }
                padContent.DockableStyle = DockingStyle;
                //padContent.DockableStyle = DockableStyle.Dockable;
                SetTitleAndIcon(pad, padContent);
                var size = GetSize(pad);
                ResizingPanel.SetEffectiveSize(padContent, size);
                if (PadClosingCommand != null)
                {
                    padContent.Closing += new EventHandler<CancelEventArgs>(padContent_Closing);
                }
                this.pads.Add(padContent);
                padContent.Show(this.DockManager, Anchor);
            }
        }
        void padContent_Closing(object sender, CancelEventArgs e)
        {
            if (PadClosingCommand.CanExecute((sender as DockableContent).Content))
            {
                PadClosingCommand.Execute((sender as DockableContent).Content);
            }
            else { e.Cancel = true; }
        }
        [Bindable(false)]
        public string HeightMemberPath
        {
            get { return (string)GetValue(HeightMemberPathProperty); }
            set { SetValue(HeightMemberPathProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HeightMemberPath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HeightMemberPathProperty =
            DependencyProperty.Register("HeightMemberPath", typeof(string), typeof(ViewModelsOnAvalonDockBehavior));


        private Size GetSize(object pad)
        {
            double defaultWidth = 0;
            double defaultHeight = 0;
            if (!string.IsNullOrWhiteSpace(WidthMemberPath))
            {
                PropertyDescriptor pd = TypeDescriptor.GetProperties(pad).Find(WidthMemberPath, false);
                if (pd != null)
                {
                    var width = pd.GetValue(pad) as double?;
                    if (width != null && width.HasValue)
                    {
                        defaultWidth = width.Value;
                    }
                }
            }
            if (!string.IsNullOrWhiteSpace(HeightMemberPath))
            {
                PropertyDescriptor pd = TypeDescriptor.GetProperties(pad).Find(HeightMemberPath, false);
                if (pd != null)
                {
                    var height = pd.GetValue(pad) as double?;
                    if (height != null && height.HasValue)
                    {
                        defaultHeight = height.Value;
                    }
                }
            }
            return new Size(defaultWidth, defaultHeight);
        }
        public AnchorStyle Anchor
        {
            get { return (AnchorStyle)GetValue(AnchorProperty); }
            set { SetValue(AnchorProperty, value); }
        }

        [Bindable(false)]
        public string WidthMemberPath
        {
            get { return (string)GetValue(WidthMemberPathProperty); }
            set { SetValue(WidthMemberPathProperty, value); }
        }

        public static readonly DependencyProperty WidthMemberPathProperty =
           DependencyProperty.Register("WidthMemberPath", typeof(string), typeof(ViewModelsOnAvalonDockBehavior));

        public static readonly DependencyProperty AnchorProperty =
            DependencyProperty.Register("Anchor", typeof(AnchorStyle), typeof(ViewModelsOnAvalonDockBehavior),
            new UIPropertyMetadata(AnchorStyle.Right));

        void OnPadsCurrentChanged(object sender, EventArgs e)
        {
            var actToolContent = pads.FirstOrDefault(x => x.Content == padCollectionView.CurrentItem);
            this.DockManager.ActiveContent = actToolContent;
        }
    }
}
